﻿//#define _CRT_SECURE_NO_WARNINGS
//#include <stdio.h>
#include "common.h"
// C/C++ 学习网站：https://legacy.cplusplus.com 注意旧版本（搜索）和新版本

// 一、结构体在内存中的对齐
/*
1. 第一个成员在与结构体变量偏移量为 0 的地址处。
2.其他成员变量要对齐到某个数字 (对齐数) 的整数倍的地址处。
    对齐数等于编译器默认的一个对齐数与该成员大小的较小值；
    VS 默认值为 8 而其他编译器没有默认值。
3.结构体总大小为最大对齐数 (每个成员变量都有一个对齐数) 的整数倍。
4.如果嵌套了结构体的情况, 嵌套的结构体对齐到自己成员的最大对齐数的整数倍处, 结构体的整
    体大小就是所有成员最大对齐数 (含嵌套结构体的对齐数) 的整数倍。
5.如果定义了字符串或数组，对齐数只由定义的类型决定，比如 char name[30] 就是 1 字节对齐。
    在比如 double arr[20] 就是 8 字节对齐；short arr[10] 就是 2 字节对齐。
    结构体成员偏移量可以通过 offsetof() 函数来获取。
所以我们在写代码的时候，尽量让结构体的成员变量对齐到对齐数的整数倍，这样可以减少内存的浪费。
在 VS 里，可以用 #pragma pack(n) 来修改默认的对齐数，n 一般为 1、2、4、8、16、32、64。
*/


// 二、位域 (也叫位段)
/*
位段是一种数据类型，它可以用来表示一个或多个二进制位。只能在结构体中使用。
位段的声明格式如下：
    类型说明符 位段名：位段长度;    // 位段长度一般为 1 到 64 位（比特位）
    这里的类型说明符必须是整型或枚举类型。
例如：
typedef struct W
{
    int _a:2; // 位段名为 _a，位段长度为 10 位（比特位）
    int _b:4; // 位段名为 _a，位段长度为 10 位（比特位）
    int _c:16; // 位段名为 _a，位段长度为 10 位（比特位）
    int _c:32; // 位段名为 _a，位段长度为 10 位（比特位）
} w;
位段的主要作用是用来节省内存空间，因为位段可以节省空间；所以在网络编程中广泛使用。
    1.位段的成员可以是 int、unsigned int、signed int、
        或者是 char(属于整形家族) 、unsigned char(属于整形家族) 、short 等类型
    2.位段的空间上是按照需要以 4 个字节 (int) 或者 1 个字节 (char) 的方式来开辟的。
        主要取决于定义成员的类型。
    3.位段涉及很多不确定因素, 位段是不跨平台的, 注重可移植的程序应该避免使用位段。
位段在内存中的布局：
    位段的内存布局是按照字节对齐的。
    位段的内存布局是按照最大对齐数的整数倍来对齐的；所以位段的总大小总是最大对齐数的整数倍。
    C语音标准没有严格定义位段的内存布局，不同编译器的实现可能不同。
位段在不同的机器上布局可能不同，跨平台的程序应该避免使用位段。
    1.int 位段被当成有符号数还是无符号数是不确定的。
    2.位段中最大位的数目不能确定。(16 位机器最大 16, 32位机器最大 32，写成 27,
        在 16 位机器会出问题。
    3.位段中的成员在内存中从左向右分配, 还是从右向左分配标准尚未定义。
    4.当一个结构包含两个位段, 第二个位段成员比较大, 无法容纳于第一个位段剩余的位时,
        是舍弃剩余的位还是利用,这是不确定的。
*/


// 三、枚举类型
/*
枚举类型是一种数据类型，它可以用来表示一组相关的常量。
枚举类型声明格式如下：
    enum 枚举名 { 枚举元素1, 枚举元素2, 枚举元素3, ... };
        例如：
        enum Color {
        RED,  // 0
        GREEN,  // 1
        BLUE  // 2
    };
在枚举常量定义的时候也可以为其赋值，例如：
    typedef enum Day {
        Mon = 1,  // 1
        Tue,  // 2
        Wed,  // 3
        Thu,  // 4
        Fri,  // 5
        Sat,  // 6
        Sun  // 7
    } day;
枚举的使用：
    enum 枚举名 变量名 = 枚举元素;
    例如：
    enum Color color = RED;
    或者：
    enum Color color = 1; // 等价于 RED
枚举元素的赋值可以省略，默认从 0 开始赋值。

枚举的取值范围是 0 到 n-1，其中 n 是枚举元素的个数。
枚举类型可以用来代替整数常量。增加代码的可读性，并减少出错的可能性。
枚举类型在内存中是以整型的方式存储的。
枚举的优点：
    1.增加代码的可读性和可维护性
    2.和#define定义的标识符比较枚举有类型检查,更加严谨
    3.防止了命名污染(封装)
    4.便于调试，枚举类型可以显示枚举元素的名称，便于调试。
        而#define定义的标识符只能显示数值。
    5.使用方便,一次可以定义多个常量
*/


// 四、联合（也叫共用体）类型
/*
联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
联合声明格式如下：(类似于结构体)
    union 联合名 { 成员1类型 成员1名, 成员2类型 成员2名, ... };
    例如：
    typedef union Data {
        int a;
        float b;
        char c;
    } data;
联合的使用：
    联合变量名.成员名 = 值;
    例如：
    data d1 = { 0 }; // 初始化 d1 变量，也可以不用赋值
    d1.a = 10;
    d1.b = 3.14;
    d1.c = 'A'; // 存在问题，这里修改了 a、b 成员内存首元素地址值。
联合的优点：
    1.节省内存空间, 因为联合变量只占用一块内存空间, 而结构体占用多个内存空间。
    2.可以用来表示不同类型的数据, 例如: 整数、浮点数、字符、结构体等。
    3.可以用来表示不同类型的变量, 例如: 整数变量、浮点数变量、字符变量、结构体变量等。
    4.可以用来表示不同类型的函数返回值, 例如: 整数、浮点数、字符、结构体等。
    5.可以用来表示不同类型的函数参数, 例如: 整数、浮点数、字符、结构体等。
联合体的成员可以是不同的数据类型,还可以是共用体类型, 也可以是结构体类型。
因为联合体的成员共享同一块内存空间, 所以联合体的成员不能同时访问,
    否则会导致不可预知的结果。也就是说, 联合体的成员不能同时读写。否则会导致数据错乱。
    而且联合体的大小至少是最大的成员的大小。
联合体在内存中也存在对齐，类似于结构体 struct。
    即每一个成员的对齐数由它自身的对齐数决定。比如 int a; 它的对齐数为 4 字节。
    但最大成员的大小不是最大对齐数的整数倍时，就要对齐到最大对齐数的整数倍位置。
*/

int main()
{
    // 结构体与位段
    {
        print_start_line("测试结构体与位段部分开始");
        // 结构体的定义、初始化、简单使用
        test_struct();
        printf("结构体复习部分完毕\n\n");

        // 结构体在内存中的对齐
        struct_align();
        printf("结构体内存对齐部分完毕\n\n");

        // 结构体作为函数参数传递
        struct Person p1 = { "Tom", 20,
            { "male", 65.6f, 172.5f, "14383838438", "China Shanghai"} };
        struct Person p2 = { "Jane", 22,
            { "male", 78.2f, 178.6f, "12424242424", "China BeiJing"} };
        struct_print(p1, &p2);
        printf("结构体作为函数参数传递部分完毕\n\n");

        // 位段
        bit_field_test();
        printf("位段部分完毕\n\n");

        print_end_line("测试结构体与位段部分结束");
    }

    // 枚举类型
    {
        print_start_line("测试枚举类型部分开始");
        // 枚举类型定义、使用
        extern void enum_test();
        enum_test();

        print_end_line("测试枚举类型部分结束");
    }

    // 联合类型
    {
        print_start_line("测试联合类型部分开始");
        // 联合类型定义、使用
        extern void union_test();
        union_test();

        print_end_line("测试联合类型部分结束");
    }

    // 课堂作业-通讯录简单版
    main_contact();

    return 0;
}